package ithink.link.base.service;

import ithink.link.base.zk.ZkMonitorFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.List;

/**
 * Created by zaqb on 17-10-23.
 */
public class LinkServerHelper {
    public static final Logger LOGGER = LoggerFactory.getLogger( LinkServerHelper.class);
    private boolean isInited = false;
    private boolean isPublished = false;

    private IConfigMonitor<LinkServerConfig> serverConfigMonitor;
    private IConfigMonitor<OnlineLinkServer> onlineServerMonitor;

    private OnlineServerListener onlineServerListener = null;

    private ServerConfigListener serverConfigListener = null;

    private ILinkServerListener listener = null;

    private static ServerState serverState = ServerState.Init;

    private String name;
    private String version = "";

    LinkServerConfig currentConfig = null;

    public LinkServerHelper(String name, String version) {
        this.name = name;
        this.version = version;
        onlineServerListener = new OnlineServerListener(this);
        serverConfigListener = new ServerConfigListener(this);
    }
    public void init(ILinkServerListener listener) throws Exception {
        if (!isInited) {
            this.listener = listener;
            serverConfigMonitor = ZkMonitorFactory.createMonitor(LinkServerConfig.class, "127.0.0.1:2181","/ithink/link/config");
            serverConfigMonitor.setListener(serverConfigListener);

            onlineServerMonitor = ZkMonitorFactory.createMonitor(OnlineLinkServer.class, "127.0.0.1:2181","/ithink/link/online");
            onlineServerMonitor.setListener(onlineServerListener);

            serverConfigMonitor.start();
            onlineServerMonitor.start();
            isInited = true;
        }
    }

    public void unInit() {
        if (isInited) {
            try {
                serverConfigMonitor.stop();
                onlineServerMonitor.stop();
            }catch (Exception ex) {
                LOGGER.error( "ex : ", ex );
            }
        }

    }

    public LinkServerConfig getCurrentConfig() throws Exception {
        if(currentConfig == null) {
            currentConfig = serverConfigMonitor.getConfig(name);
        }
        return currentConfig;
    }

    public void publish() throws Exception {
        if (isInited)
        {
            try {
                currentConfig = serverConfigMonitor.getConfig(name);
                if(currentConfig == null) {
                    throw new RuntimeException( "can't find server config by name :" + name );
                }
                OnlineLinkServer onlineLinkServer = new OnlineLinkServer();
                onlineLinkServer.setServerName( currentConfig.getServerName() );
                onlineLinkServer.setDate(new Date());
                onlineLinkServer.setVersion( version );
                while (true){
                    boolean tryNext = onlineServerMonitor.tryCreateTemporaryPath( name, onlineLinkServer );
                    if(tryNext) {
                        break;
                    }else {
                        LOGGER.info( "try create temporary path {} failed", name );
                        Thread.sleep( 5*1000 );
                    }
                }
            } catch (Exception e) {
                throw e;
            }
            isPublished = true;
            changeState(ServerState.Ready);
        } else {
            throw new Exception("init helper before publish");
        }
    }

    private void changeState(ServerState state) {
        ServerState old = serverState;
        serverState = state;
        LOGGER.info(name + " server state from {} to {}", old, state);
        listener.onStateChanged(currentConfig, old, state);
    }


    private static class OnlineServerListener implements IConfigMonitor.IListener<OnlineLinkServer> {

        private OnlineServerListener(LinkServerHelper serverHelper) {
            this.serverHelper = serverHelper;
        }

        private LinkServerHelper serverHelper;
        @Override
        public void onObjectCreated(List<OnlineLinkServer> created) {
            LOGGER.info("global config created");
            if(!serverHelper.isPublished) {
                return;
            }
            for (OnlineLinkServer server : created) {
                if (server != null) {
                    serverHelper.changeState( ServerState.Active );
                }
            }
        }

        @Override
        public void onObjectDeleted(List<OnlineLinkServer> deleted) {
            if(!serverHelper.isPublished) {
                return;
            }
            LOGGER.info("global config delete");
            for(OnlineLinkServer server: deleted) {
                if(server != null) {
                    serverHelper.changeState(ServerState.Delete);
                }
            }
        }

        @Override
        public void onObjectUpdated(List<OnlineLinkServer> updated) {

            LOGGER.info("global config update");
        }

        @Override
        public void onNewSession() {

        }

        @Override
        public void onSessionExpired() {
            serverHelper.changeState(ServerState.Expired);
        }

        @Override
        public void onError() {
            serverHelper.changeState( ServerState.Error);
        }
    }

    private static class ServerConfigListener implements IConfigMonitor.IListener<LinkServerConfig> {

        private ServerConfigListener(LinkServerHelper serverHelper) {
            this.serverHelper = serverHelper;
        }
        private LinkServerHelper serverHelper;
        @Override
        public void onObjectCreated(List<LinkServerConfig> created) {
            if(!serverHelper.isPublished) {
                return;
            }LOGGER.info("global config created");
        }

        @Override
        public void onObjectDeleted(List<LinkServerConfig> deleted) {
            if(!serverHelper.isPublished) {
                return;
            }LOGGER.info("global config delete");
        }

        @Override
        public void onObjectUpdated(List<LinkServerConfig> updated) {
            LOGGER.info("config update");
            if(!serverHelper.isPublished) {
                return;
            }
            for(LinkServerConfig server: updated) {
                if(server != null && server.getServerName().equals(serverHelper.currentConfig.getServerName())) {
                    serverHelper.currentConfig = server;
                    serverHelper.listener.onConfigChanged( server );
                }
            }
        }

        @Override
        public void onNewSession() {
        }

        @Override
        public void onSessionExpired() {
        }

        @Override
        public void onError() {
        }
    }
}
